﻿using System;
using System.Text;
using System.Reflection;
using System.Collections;
using System.Linq;
using System.Data;
using System.Collections.Generic;
using UMC.Net;

namespace UMC.Data
{
    /// <summary>
    /// JSON解析工具
    /// </summary>
    public sealed partial class JSON
    {



        /// <summary>
        /// JSON反序列化
        /// </summary>
        /// <param name="json">JSON</param>
        /// <returns></returns>
        public static T Deserialize<T>(ReadOnlySpan<byte> json) where T : new()
        {
            int i = -1;
            return (T)Deserialize<T>(ref json, ref i);
        }
        public static T Deserialize<T>(StringValue json) where T : new()
        {
            if (json != null)
            {
                return (T)Deserialize<T>(json.Span);
            }
            return default(T);
        }
        public static T[] Deserializes<T>(ReadOnlySpan<byte> json) where T : new()
        {
            int i = -1;

            return Deserializes<T>(ref json, ref i);
        }
        static T[] Deserializes<T>(ref ReadOnlySpan<byte> input, ref int index) where T : new()
        {

            IList<T> list = new List<T>();
            for (index++; index < input.Length; index++)
            {

                switch (input[index])
                {
                    case (byte)'[':
                        RemoveEmpty(ref input, ref index);
                        if (input[index + 1] == ']')
                        {
                            index++;
                            return list.ToArray();
                        }


                        list.Add((T)Deserialize<T>(ref input, ref index));

                        break;
                    case (byte)']':
                        return list.ToArray();
                    case (byte)'{':
                        --index;
                        list.Add((T)Deserialize<T>(ref input, ref index));
                        break;
                    case (byte)',':
                        list.Add((T)Deserialize<T>(ref input, ref index));
                        break;
                    case (byte)'\t':
                    case (byte)' ':
                    case (byte)'\b':
                    case (byte)'\r':
                    case (byte)'\n':
                        break;
                    default:
                        throw new System.FormatException("非数组格式");
                }

            }
            return list.ToArray();

        }
        /// <summary>
        /// JSON反序列化
        /// </summary>
        /// <param name="json">json</param>
        /// <param name="type">转化的类型</param>
        /// <returns></returns>
        public static object Deserialize(ReadOnlySpan<byte> json, Type type)
        {
            int i = -1;
            return Deserialize(ref json, ref i, type);
        }
        /// <summary>
        /// JSON字典化
        /// </summary>
        /// <param name="json">JSON</param>
        /// <returns></returns>
        public static object Deserialize(ReadOnlySpan<byte> json)
        {
            int i = -1;
            return Deserialize(ref json, ref i);
        }

        static object Deserialize<T>(ref ReadOnlySpan<byte> input, ref int index) where T : new()
        {

            var isArray = false;
            var isObject = false;
            IList list = null;
            var type = typeof(T);
            object objValue = null;
            for (index++; index < input.Length; index++)
            {

                switch (input[index])
                {
                    case (byte)'[':
                        isArray = true;
                        if (type.IsGenericType)
                        {
                            objValue = new T();
                            list = objValue as IList;

                            RemoveEmpty(ref input, ref index);
                            if (input[index + 1] == ']')
                            {
                                index++;
                                return objValue;
                            }
                            var gType = type.GetGenericArguments()[0];

                            list.Add(Deserialize(ref input, ref index, gType));

                        }
                        else if (type.IsArray)
                        {

                            objValue = list = new ArrayList();
                            var gType = type.GetElementType();
                            RemoveEmpty(ref input, ref index);
                            if (input[index + 1] == ']')
                            {
                                index++;
                                return Array.CreateInstance(gType, 0);
                            }
                            list.Add(Deserialize(ref input, ref index, gType));

                        }
                        else
                        {
                            throw new System.ArrayTypeMismatchException(type.ToString());
                        }
                        break;
                    case (byte)'{':
                        if (type == typeof(Hashtable))
                        {
                            index--;
                            return Deserialize(ref input, ref index);
                        }
                        else if (type.IsArray)
                        {
                            var eType = type.GetElementType();
                            var arr = Array.CreateInstance(eType, 1);
                            index--;
                            arr.SetValue(Deserialize(ref input, ref index, eType), 0);
                            return arr;
                        }
                        isObject = true;
                        RemoveEmpty(ref input, ref index);
                        if (input[index + 1] == '}')
                        {
                            index++;
                            return new T();
                        }
                        var key = Deserialize(ref input, ref index).ToString();
                        if (key == Constructor)
                        {
                            objValue = ((T)Reflection.CreateInstance(Deserialize(ref input, ref index).ToString())) ?? new T();

                            // objValue = new T();
                            break;
                        }
                        objValue = new T();

                        DeserializeObject(key, ref input, ref index, objValue);
                        break;
                    case (byte)',':
                        if (isArray)
                        {
                            if (type.IsArray)
                            {
                                list.Add(Deserialize(ref input, ref index, type.GetElementType()));
                            }
                            else if (type.IsGenericType)
                            {
                                list.Add(Deserialize(ref input, ref index, type.GetGenericArguments()[0]));
                            }
                            else
                            {
                                Deserialize(ref input, ref index);
                            }
                        }
                        else if (isObject)
                        {
                            DeserializeObject(Deserialize(ref input, ref index) as string, ref input, ref index, objValue);
                        }
                        break;
                    case (byte)']':
                        if (isArray)
                        {
                            if (type.IsArray)
                            {
                                return ((ArrayList)list).ToArray(type.GetElementType());
                            }
                            return objValue;
                        }
                        else
                        {
                            throw new System.FormatException("非JSON格式");
                        }
                    case (byte)'}':
                        if (isObject)
                        {
                            return objValue;
                        }
                        else
                        {
                            throw new System.FormatException("非JSON格式");
                        }
                    case (byte)'\b':
                    case (byte)' ':
                    case (byte)'\r':
                    case (byte)'\n':
                    case (byte)'\t':
                    case (byte)':':
                        break;
                    case (byte)'"':
                    case (byte)'\'':
                        return Reflection.Parse(Deserialize(ref input, ref index, input[index]), type);

                    case (byte)'$':
                    case (byte)'_':
                    case (byte)'.':
                    case (byte)'-':
                        return Data.Reflection.Parse(Deserialize2(ref input, ref index), type);
                    default:
                        var c = Deserialize2(ref input, ref index);

                        switch (c)
                        {
                            case "undefined":
                            case "null":
                                return null;
                            default:

                                return Reflection.Parse(c, type);
                        }
                }

            }
            return objValue;

        }
        //Func
        static object Deserialize(ref ReadOnlySpan<byte> input, ref int index, Func<Object> type)
        {

            var isArray = false;
            var isObject = false;
            ArrayList list = null;

            object objValue = null;
            for (index++; index < input.Length; index++)
            {

                switch (input[index])
                {
                    case (byte)'[':
                        {
                            isArray = true;

                            objValue = list = new ArrayList();
                            RemoveEmpty(ref input, ref index);
                            if (input[index + 1] == ']')
                            {
                                index++;
                                return Array.CreateInstance(type().GetType(), 0);
                            }
                            list.Add(Deserialize(ref input, ref index, type));

                        }
                        break;
                    case (byte)'{':
                        isObject = true;
                        RemoveEmpty(ref input, ref index);
                        if (input[index + 1] == '}')
                        {
                            index++;
                            return type();
                        }
                        var key = Deserialize(ref input, ref index).ToString();
                        if (key == JSON.Constructor)
                        {
                            String tValue = Deserialize(ref input, ref index).ToString();
                            objValue = Reflection.CreateInstance(tValue) ?? type();
                            break;
                        }
                        objValue = type();

                        DeserializeObject(key, ref input, ref index, objValue);
                        break;
                    case (byte)',':
                        if (isArray)
                        {
                            list.Add(Deserialize(ref input, ref index, type));

                        }
                        else if (isObject)
                        {
                            DeserializeObject(Deserialize(ref input, ref index) as string, ref input, ref index, objValue);
                        }
                        break;
                    case (byte)']':
                        if (isArray)
                        {
                            if (list.Count > 0)
                            {
                                return list.ToArray(type().GetType());
                            }
                            return objValue;
                        }
                        else
                        {
                            throw new System.FormatException("非JSON格式");
                        }
                    case (byte)'}':
                        if (isObject)
                        {
                            return objValue;
                        }
                        else
                        {
                            throw new System.FormatException("非JSON格式");
                        }
                    case (byte)'\b':
                    case (byte)' ':
                    case (byte)'\r':
                    case (byte)'\n':
                    case (byte)'\t':
                    case (byte)':':
                        break;
                    case (byte)'"':
                    case (byte)'\'':
                        return Deserialize(ref input, ref index, input[index]);

                    case (byte)'$':
                    case (byte)'_':
                    case (byte)'.':
                    case (byte)'-':
                        return Reflection.Parse(Deserialize2(ref input, ref index), type());
                    default:
                        index--;
                        return Deserialize(ref input, ref index);
                }

            }
            return objValue;

        }

        //Func
        static void DeserializeObject(string key, ref ReadOnlySpan<byte> input, ref int index, object objValue)
        {
            if (objValue is IJSON)
            {
                if (objValue is IJSONType)
                {
                    ((IJSON)objValue).Read(key, Deserialize(ref input, ref index, ((IJSONType)objValue).GetInstance(key)));
                }
                else
                {
                    ((IJSON)objValue).Read(key, Deserialize(ref input, ref index));
                }
            }
            else if (objValue is IDictionary)
            {
                ((IDictionary)objValue)[key] = Deserialize(ref input, ref index);
            }
            else if (objValue is Record)
            {
                ((Record)objValue).SetValue(key, Deserialize(ref input, ref index));
            }
            else
            {
                var prototyType = objValue.GetType().GetProperty(key
                    , BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.NonPublic | BindingFlags.Instance);
                if (prototyType != null && prototyType.CanWrite)
                {
                    prototyType.SetValue(objValue, Deserialize(ref input, ref index, prototyType.PropertyType), null);
                }
                else
                {
                    Deserialize(ref input, ref index);
                }
            }
        }
        static void DeserializeObject(ref ReadOnlySpan<byte> input, ref int index, object objValue)
        {

            for (index++; index < input.Length; index++)
            {

                switch (input[index])
                {

                    case (byte)',':

                        DeserializeObject(Deserialize(ref input, ref index) as string, ref input, ref index, objValue);


                        break;
                    case (byte)'}':
                        return;
                    case (byte)'\b':
                    case (byte)' ':
                    case (byte)'\r':
                    case (byte)'\n':
                    case (byte)'\t':
                    case (byte)':':
                        break;
                    default:
                        Deserialize2(ref input, ref index);
                        break;
                }

            }

        }

        static object Deserialize(ref ReadOnlySpan<byte> input, ref int index, Type type)
        {

            var isArray = false;
            var isObject = false;
            IList list = null;

            object objValue = null;
            for (index++; index < input.Length; index++)
            {

                switch (input[index])
                {
                    case (byte)'[':
                        isArray = true;
                        if (type.IsGenericType)
                        {
                            objValue = Reflection.CreateInstance(type);
                            list = objValue as IList;

                            RemoveEmpty(ref input, ref index);
                            if (input[index + 1] == ']')
                            {
                                index++;
                                return objValue;
                            }
                            var gType = type.GetGenericArguments()[0];

                            list.Add(Deserialize(ref input, ref index, gType));

                        }
                        else if (type.IsArray)
                        {

                            objValue = list = new ArrayList();
                            var gType = type.GetElementType();
                            RemoveEmpty(ref input, ref index);
                            if (input[index + 1] == ']')
                            {
                                index++;
                                return Array.CreateInstance(gType, 0);
                            }
                            list.Add(Deserialize(ref input, ref index, gType));

                        }
                        else
                        {
                            throw new System.ArrayTypeMismatchException(type.ToString());
                        }
                        break;
                    case (byte)'{':
                        if (type.IsArray)
                        {
                            var eType = type.GetElementType();
                            var arr = Array.CreateInstance(eType, 1);
                            index--;
                            arr.SetValue(Deserialize(ref input, ref index, eType), 0);
                            return arr;
                        }
                        isObject = true;
                        RemoveEmpty(ref input, ref index);
                        if (input[index + 1] == '}')
                        {
                            index++;
                            return Reflection.CreateInstance(type);
                        }
                        var key = Deserialize(ref input, ref index).ToString();
                        if (key == Constructor)
                        {
                            objValue = Reflection.CreateInstance(Deserialize(ref input, ref index).ToString()) ?? Reflection.CreateInstance(type);
                            break;
                        }
                        objValue = Reflection.CreateInstance(type);
                        DeserializeObject(key, ref input, ref index, objValue);
                        break;
                    case (byte)',':
                        if (isArray)
                        {
                            if (type.IsArray)
                            {
                                list.Add(Deserialize(ref input, ref index, type.GetElementType()));
                            }
                            else if (type.IsGenericType)
                            {
                                list.Add(Deserialize(ref input, ref index, type.GetGenericArguments()[0]));
                            }
                            else
                            {
                                Deserialize(ref input, ref index);
                            }
                        }
                        else if (isObject)
                        {
                            DeserializeObject(Deserialize(ref input, ref index) as string, ref input, ref index, objValue);

                        }
                        break;
                    case (byte)']':
                        if (isArray)
                        {
                            if (type.IsArray)
                            {
                                return ((ArrayList)list).ToArray(type.GetElementType());
                            }
                            return objValue;
                        }
                        else
                        {
                            throw new System.FormatException("非JSON格式");
                        }
                    case (byte)'}':
                        if (isObject)
                        {
                            return objValue;
                        }
                        else
                        {
                            throw new System.FormatException("非JSON格式");
                        }
                    case (byte)'\b':
                    case (byte)' ':
                    case (byte)'\r':
                    case (byte)'\n':
                    case (byte)'\t':
                    case (byte)':':
                        break;
                    case (byte)'"':
                    case (byte)'\'':
                        return Reflection.Parse(Deserialize(ref input, ref index, input[index]), type);

                    case (byte)'$':
                    case (byte)'_':
                    case (byte)'.':
                    case (byte)'-':
                        return Reflection.Parse(Deserialize2(ref input, ref index), type);
                    default:
                        var c = Deserialize2(ref input, ref index);

                        switch (c)
                        {
                            case "undefined":
                            case "null":
                                return null;
                            default:

                                return Reflection.Parse(c, type);
                        }
                }

            }
            return objValue;

        }

        static void RemoveEmpty(ref ReadOnlySpan<byte> input, ref int index)
        {
            var isb = false;
            for (index++; index < input.Length; index++)
            {
                switch (input[index])
                {
                    case (byte)'\t':
                    case (byte)' ':
                    case (byte)'\b':
                    case (byte)'\r':
                    case (byte)'\n':
                        break;
                    default:
                        isb = true;
                        break;
                }
                if (isb)
                {
                    index--;
                    break;
                }
            }


        }


        /// <summary>
        /// 反序列化
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        public static T Deserialize<T>(ReadOnlySpan<byte> input, ref int startIndex)
        {
            return (T)Deserialize(ref input, ref startIndex, typeof(T));
        }
        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="input"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        public static object Deserialize(ReadOnlySpan<byte> input, ref int startIndex)
        {

            startIndex--;
            return Deserialize(ref input, ref startIndex);
        }
        static object Deserialize(ref ReadOnlySpan<byte> input, ref int index)
        {
            var isArray = false;
            var isObject = false;
            ArrayList list = null;
            Hashtable hask = null;
            for (index++; index < input.Length; index++)
            {

                switch (input[index])
                {
                    case (byte)'[':
                        isArray = true;
                        list = new ArrayList();
                        RemoveEmpty(ref input, ref index);
                        if (input[index + 1] == ']')
                        {
                            index++;
                            return list.ToArray();
                        }
                        list.Add(Deserialize(ref input, ref index));

                        break;
                    case (byte)'{':
                        isObject = true;
                        // var beginIndex = index;
                        RemoveEmpty(ref input, ref index);
                        if (input[index + 1] == '}')
                        {
                            index++;
                            return hask ?? new Hashtable();
                        }
                        var key = Deserialize(ref input, ref index).ToString();
                        if (key == Constructor)
                        {
                            var convValue = Deserialize(ref input, ref index).ToString();

                            var type = Reflection.CreateInstance(convValue);//Deserialize(ref input, ref index).ToString());
                            // index = beginIndex - 1;
                            if (type != null)
                            {
                                DeserializeObject(ref input, ref index, type);
                                return type;
                            }
                            else
                            {
                                hask = new Hashtable();
                                hask[key] = convValue;// Deserialize(ref input, ref index);
                            }
                        }
                        else
                        {
                            hask = new Hashtable();
                            hask[key] = Deserialize(ref input, ref index);
                        }
                        break;
                    case (byte)',':
                        if (isArray)
                        {
                            list.Add(Deserialize(ref input, ref index));
                        }
                        else if (isObject)
                        {
                            var key2 = Deserialize(ref input, ref index);
                            hask[key2] = Deserialize(ref input, ref index);
                        }
                        break;
                    case (byte)']':
                        if (isArray)
                        {
                            return list.ToArray();
                        }
                        else
                        {
                            throw new System.FormatException("非JSON格式");
                        }
                    case (byte)'}':
                        if (isObject)
                        {
                            return hask;
                        }
                        else
                        {
                            throw new System.FormatException("非JSON格式");
                        }
                    case (byte)'\b':
                    case (byte)' ':
                    case (byte)'\r':
                    case (byte)'\n':
                    case (byte)'\t':
                    case (byte)':':
                        break;
                    case (byte)'"':
                    case (byte)'\'':
                        return Deserialize(ref input, ref index, input[index]);

                    case (byte)'$':
                    case (byte)'_':
                    case (byte)'.':
                        return Deserialize2(ref input, ref index);
                    default:

                        var c = Deserialize2(ref input, ref index);
                        switch (c)
                        {
                            case "true":
                                return true;
                            case "false":
                                return false;
                            case "undefined":
                            case "null":
                                return null;
                            default:
                                return c;
                        }
                }

            }
            throw new System.FormatException("非JSON格式");
        }
        static string Deserialize2(ref ReadOnlySpan<byte> input, ref int index)
        {
            var b = index;
            for (var i = index + 1; i < input.Length; i++)
            {
                switch (input[i])
                {
                    case (byte)']':
                    case (byte)'}':
                    case (byte)':':
                    case (byte)',':
                        index = i - 1;
                        return input.Slice(b, i - b).UTF8().Trim();

                }
            }
            index = input.Length - 1;
            return input.Slice(b).UTF8().Trim();

        }
        static string Deserialize(ref ReadOnlySpan<byte> input, ref int index, byte cher)
        {
            index++;

            var isTo = false;
            var sb = new List<byte>();

            for (; index < input.Length; index++)
            {
                var ichar = input[index];
                if (ichar == '\\')
                {
                    if (isTo)
                    {
                        sb.Add(ichar);
                        isTo = false;
                    }
                    else
                    {
                        isTo = true;
                    }

                }
                else if (isTo)
                {
                    isTo = false;
                    switch (ichar)
                    {
                        case (byte)'u':
                            byte[] codes = new byte[2];
                            Utility.TryParse(input.Slice(index + 1, 2), 16, out int b1);
                            Utility.TryParse(input.Slice(index + 3, 2), 16, out int b2);
                            codes[1] = (byte)b1;//Utility.TryParse(input.Slice(index + 1, 2), 16) Convert.ToByte(input.Substring(index + 1, 2), 16);
                            codes[0] = (byte)b2;// Convert.ToByte(input.Substring(index + 3, 2), 16);
                            // sb.Append(Encoding.Unicode.GetString(codes));
                            sb.AddRange(codes);
                            index += 4;
                            break;
                        case (byte)'n':
                            sb.Add((byte)'\n');
                            break;
                        case (byte)'r':
                            sb.Add((byte)'\r');
                            break;
                        case (byte)'t':
                            sb.Add((byte)'\t');
                            break;
                        case (byte)'f':
                            sb.Add((byte)'\f');
                            break;
                        case (byte)'b':
                            sb.Add((byte)'\b');
                            break;
                        case (byte)'a':
                            sb.Add((byte)'\a');
                            break;
                        case (byte)'0':
                            sb.Add((byte)'\0');
                            break;
                        case (byte)'v':
                            sb.Add((byte)'\v');
                            break;
                        default:
                            sb.Add(ichar);
                            break;
                    }
                }
                else if (ichar == cher)
                {
                    break;

                }
                else
                {
                    sb.Add(ichar);
                    isTo = false;
                }
            }
            if (index != input.Length)
            {
                // sb.
                return sb.ToArray().UTF8();//.ToString();
            }
            throw new System.FormatException("非JSON格式");

        }


    }
}
